package cn.tnar.parkservice.util;

import okhttp3.*;
import okio.BufferedSink;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author xzhang
 */
public class HttpUtil {

    private static final Logger log = LoggerFactory.getLogger(HttpUtil.class);
    private static final OkHttpClient CLIENT = new OkHttpClient.Builder()
            .addInterceptor(chain -> {
                Request request = chain.request().newBuilder().addHeader("Connection", "close").build();
                return chain.proceed(request);
            })
            .connectTimeout(15, TimeUnit.SECONDS)
            .readTimeout(15, TimeUnit.SECONDS)
            .build();

    private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");


    private static Request buildFormRawRequest(String url, String body, Map<String, Object> header) {
        Headers.Builder hbuilder = new Headers.Builder();
        header.forEach((key, value) -> hbuilder.add(key, String.valueOf(value)));
        RequestBody rb = new RequestBody() {
            @Override
            public MediaType contentType() {
                return MediaType.parse("application/x-www-form-urlencoded; charset=utf-8");
            }

            @Override
            public void writeTo(BufferedSink sink) throws IOException {
                sink.writeString(body, Charset.forName("utf-8"));
            }
        };
        return new Request.Builder()
                .url(url)
                .headers(hbuilder.build())
                .post(rb)
                .build();
    }

    private static Request buildPostRequest(String url, Map<String, Object> body) {
        FormBody.Builder builder = new FormBody.Builder();
        body.forEach((key, value) -> builder.add(key, String.valueOf(value)));
        return new Request.Builder()
                .url(url)
                .post(builder.build())
                .build();
    }

    private static Request buildPostJsonRequest(String url, String json) {
        RequestBody requestBody = RequestBody.create(JSON, json);
        return new Request.Builder()
                .url(url)
                .post(requestBody)
                .build();
    }

    public static void post(String url, Map<String, Object> body) {

        try (Response response = CLIENT.newCall(buildPostRequest(url, body)).execute()) {
            if (!response.isSuccessful()) {
                log.error("post: {} to {} not successful.", body, url);
            } else {
                log.info("result: {}", response.body().string());
            }
        } catch (IOException e) {
            log.error("post: {} to {} error.", body, url);
        }
    }

    public static void postJson(String url, String postBody) {

        try (Response response = CLIENT.newCall(buildPostJsonRequest(url, postBody)).execute()) {
            if (!response.isSuccessful()) {
                log.error("post: {} to {} not successful.", postBody.substring(0, 128), url);
            } else {
                log.info("result: {}", response.body().string());
            }
        } catch (IOException e) {
            log.error("post: {} to {} error.", postBody.substring(0, 128), url);
        }
    }

    public static void postAsync(String url, Map<String, Object> body) {
        CLIENT.newCall(buildPostRequest(url, body)).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                log.error("post: {} to {} error.", body, url);
                log.error("error is ", e);
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                try {
                    if (!response.isSuccessful()) {
                        log.error("post: {} to {} not successful.", body, url);
                    } else {
                        log.info("result: {}", response.body().string());
                    }
                } finally {
                    response.close();
                }
            }
        });
    }

    public static String postJsonWithResponse(String url, String postBody) {

        try (Response response = CLIENT.newCall(buildPostJsonRequest(url, postBody)).execute()) {
            if (!response.isSuccessful()) {
                log.error("post: {} to {} not successful.", postBody, url);
                return null;
            }
            ResponseBody body = response.body();
            if (body != null) {
                return body.string();
            }
        } catch (IOException e) {
            log.error("post: {} to {} error.", postBody, url);
        }
        return null;
    }

    public static void postJsonAsync(String url, String postBody) {
        CLIENT.newCall(buildPostJsonRequest(url, postBody)).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                log.error("post: {} to {} error.", postBody.substring(0, 128), url);
                log.error("error is ", e);
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                try {
                    if (!response.isSuccessful()) {
                        log.error("post: {} to {} not successful.", postBody.substring(0, 128), url);
                    } else {
                        log.info("result: {}", response.body().string());
                    }
                } finally {
                    response.close();
                }
            }
        });
    }

    public static void postRaw(String url, String body, Map<String, Object> header) {
        CLIENT.newCall(buildFormRawRequest(url, body, header)).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                log.error("post: header {} and body {}  to {} error.", header, body, url);
                log.error("error is ", e);
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                try {
                    if (!response.isSuccessful()) {
                        log.error("post: header {} and body {}  to {} not successful.", header, body, url);
                    } else {
                        log.debug("result: {}", URLDecoder.decode(response.body().string(), "UTF-8"));
                    }
                } finally {
                    response.close();
                }
            }
        });
    }

    public static String getWithResponse(String url) {
        try (Response response = CLIENT.newCall(
                new Request.Builder().url(url).get().build()
        ).execute()) {
            if (!response.isSuccessful()) {
                log.error("get: from {} not successful.", url);
                return null;
            }
            ResponseBody body = response.body();
            if (body != null) {
                return body.string();
            }
        } catch (IOException e) {
            log.error("get: from {} error.", url);
        }
        return null;
    }
}
